package session

import (
	"sync"
	"strings"
	"encoding/base64"
)

// 用户会话添加、删除观察者接口
type SessionObserver interface {
	OnSessionAdd(*Session)
	OnSessionClose(*Session)
}

func IsWebReq(addr string) bool {
	return strings.HasPrefix(addr, "webreq")
}

func IsUdpReq(addr string) bool {
	return strings.HasPrefix(addr, "UDP")
}

// 用户会话管理器
type SessionManager struct {
	sessions 	map[interface{}]*Session	// 玩家连接会话，playerId 作为 key

	sessionGroups	map[string][]*Session 	// 消息组列表

	closers		[]SessionObserver 			// 会话关闭监听器列表

	sessionMu	sync.RWMutex				// 会话线程锁
}


func NewSessionManager() *SessionManager {
	s := &SessionManager{}
	s.sessions = make(map[interface{}]*Session)
	s.sessionGroups = make(map[string][]*Session)
	return s
}

func (s *SessionManager) AddPlayer(session *Session) {
	oldSession := s.addPlayer(session)

	if nil != oldSession {
		oldSession.doClose()
	} else {
		for _, v := range s.closers {
			v.OnSessionAdd(session)
		}
	}
}

func (s *SessionManager) addPlayer(session *Session) *Session {
	s.sessionMu.Lock()
	defer s.sessionMu.Unlock()

	var oldSession *Session
	id := session.GetUserId()
	if nil != id {
		if ses, ok := s.sessions[id]; ok {
			if ses != session {
				oldSession = ses
				s.sessions[id] = session
			}
		} else {
			s.sessions[id] = session
		}
	}
	
	return oldSession
}

func (s *SessionManager) AddPlayerWithoutObs(session *Session) {
	s.sessionMu.Lock()
	defer s.sessionMu.Unlock()
	id := session.GetUserId()
	if nil != id {
		s.sessions[id] = session
	}
}

func (s *SessionManager) RemovePlayer(playerId interface{}) {
	session := s._removePlayer(playerId)

	if nil != session {
		session.doClose()
		if nil != session.GetUserData() {
			s.closeSession(session)
		}
	}
}

func (s *SessionManager) removePlayer(playerId interface{}) {
	session := s._removePlayer(playerId)

	if nil != session && nil != session.GetUserData() {
		s.closeSession(session)
	}
}

func (s *SessionManager) _removePlayer(playerId interface{}) *Session {
	var session *Session
	s.sessionMu.Lock()
	defer s.sessionMu.Unlock()

	if ses, ok := s.sessions[playerId]; ok {
		session = ses
		delete(s.sessions, playerId)

		for _, v := range ses.groups {
			s.quitGroup(ses, v)
		}
		ses.clearGroupName()
	}

	return session
}

func (s *SessionManager) removeSession(ses *Session, playerId interface{}) {
	s.sessionMu.Lock()

	if nil != ses && nil != ses.GetUserData() {
		if session, ok := s.sessions[playerId]; ok && ses == session {
			delete(s.sessions, playerId)
		}
	}

	if len(ses.groups) > 0 {
		for _, v := range ses.groups {
			s.quitGroup(ses, v)
		}
		ses.clearGroupName()
	}

	s.sessionMu.Unlock()

	if nil != ses && nil != ses.GetUserData() {
		s.closeSession(ses)
	}
}

func (s *SessionManager) GetPlayerByAddr(addr string) *Session {
	s.sessionMu.RLock()
	defer s.sessionMu.RUnlock()

	for _, v := range s.sessions {
		if v.Addr() == addr {
			return v
		}
	}

	return nil
}

func (s *SessionManager) GetPlayer(playerId interface{}) *Session {
	s.sessionMu.RLock()
	defer s.sessionMu.RUnlock()

	if session, ok := s.sessions[playerId]; ok {
		return session
	}
	return nil
}

func (s *SessionManager) GetAllPlayers() []*Session {
	s.sessionMu.RLock()
	defer s.sessionMu.RUnlock()

	if len(s.sessions) > 0 {
		ret := make([]*Session, 0, len(s.sessions))
		for _, v := range s.sessions {
			ret = append(ret, v)
		}
		return ret
	}
	return nil
}

func (s *SessionManager) GetPlayerCount() int {
	s.sessionMu.RLock()
	defer s.sessionMu.RUnlock()

	return len(s.sessions)
}

// 使会话加入群组
func (s *SessionManager) JoinGroup(ses *Session, name string) {
	s.sessionMu.Lock()
	defer s.sessionMu.Unlock()

	if ses.IsClose() {
		return
	}

	if g, ok := s.sessionGroups[name]; ok {
		for _, v := range g {
			if v == ses {
				return
			}
		}
		s.sessionGroups[name] = append(g, ses)
	} else {
		g := make([]*Session, 0, 100)
		g = append(g, ses)
		s.sessionGroups[name] = g
	}

	ses.addGroupName(name)
}

func (s *SessionManager) QuitGroup(ses *Session, name string) {
	s.sessionMu.Lock()
	defer s.sessionMu.Unlock()

	s.quitGroup(ses, name)

	ses.removeGroupName(name)
}

func (s *SessionManager) QuitAllGroup(ses *Session) {
	s.sessionMu.Lock()
	defer s.sessionMu.Unlock()
	for k, _ := range ses.groups {
		s.quitGroup(ses, k)
	}
	ses.clearGroupName()
}

func (s *SessionManager) quitGroup(ses *Session, name string) {
	if g, ok := s.sessionGroups[name]; ok {
		for k, v := range g {
			if v == ses {
				s.sessionGroups[name] = append(g[:k], g[k + 1:]...)
				break
			}
		}
	}
}

func (s *SessionManager) RemoveGroup(name string) {
	s.sessionMu.Lock()
	defer s.sessionMu.Unlock()

	if g, ok := s.sessionGroups[name]; ok {
		for _, v := range g {
			v.removeGroupName(name)
		}
		delete(s.sessionGroups, name)
	}
}

func (s *SessionManager) GetCountOfGroup(name string) int {
	s.sessionMu.Lock()
	defer s.sessionMu.Unlock()
	if g, ok := s.sessionGroups[name]; ok {
		return len(g)
	}
	return 0
}

func (s *SessionManager) Update() {
	// s.sessionMu.Lock()
	// defer s.sessionMu.Unlock()
	// for k, session := range s.sessions {
	// 	session.Update()
	// 	// 删除超时玩家
	// 	if !session.IsOnline {
	// 		delete(s.sessions, k)
	// 		delete(s.addrs, session.Addr)
	// 	}
	// }
}

func (s *SessionManager) AddObserver(c SessionObserver) {
	s.closers = append(s.closers, c)
}

func (s *SessionManager) RemoveObserver(c SessionObserver) {
	for k, v := range s.closers {
		if v == c {
			s.closers = append(s.closers[:k], s.closers[k+1:]...)
			break
		}
	}
}

func (s *SessionManager) closeSession(ses *Session) {
	for _, v := range s.closers {
		v.OnSessionClose(ses)
	}
}

func (s *SessionManager) Notify(buf []byte) {
	s.sessionMu.Lock()
	defer s.sessionMu.Unlock()

	buf64 := s.getBuf64(buf)

	for _, v := range s.sessions {
		if nil != v.GetUserData() {
			v.SendBufWithBase64(buf, buf64)
		}
	}
}

// 派发消息到群组，excepter: 不给这个会话派发
func (s *SessionManager) NotifyGroup(name string, buf []byte, excepter interface{}) {
	s.sessionMu.Lock()
	defer s.sessionMu.Unlock()

	buf64 := s.getBuf64(buf)
	if g, ok := s.sessionGroups[name]; ok {
		for _, v := range g {
			if v.GetUserId() != excepter {
				v.SendBufWithBase64(buf, buf64)
			}
		}
	}
}

func (s *SessionManager) NotifyGroupExcept(name string, buf []byte, excepters []interface{}) {
	s.sessionMu.Lock()
	defer s.sessionMu.Unlock()

	buf64 := s.getBuf64(buf)
	flag := true
	if g, ok := s.sessionGroups[name]; ok {
		for _, v := range g {
			flag = true
			for _, e := range excepters {
				if v.GetUserId() == e {
					flag = false
					break
				}
			}
			if flag {
				v.SendBufWithBase64(buf, buf64)
			}
		}
	}
}

func (s *SessionManager) getGroupSessions(name string) []*Session {
	s.sessionMu.Lock()
	defer s.sessionMu.Unlock()

	if g, ok := s.sessionGroups[name]; ok {
		if len(g) > 0 {
			ret := make([]*Session, len(g))
			copy(ret, g)
			return ret
		}
	}
	return nil
}

func (s *SessionManager) OnFunc(fn func(*Session)) {
	s.sessionMu.Lock()
	defer s.sessionMu.Unlock()

	for _, v := range s.sessions {
		if !v.IsClose() {
			fn(v)
		}
	}
}

func (s *SessionManager) getBuf64(buf []byte) []byte {
	buf64 := make([]byte, base64.StdEncoding.EncodedLen(len(buf)))
	base64.StdEncoding.Encode(buf64, buf)
	return buf64
}

var mgr = NewSessionManager()

func AddPlayer(session *Session) {
	mgr.AddPlayer(session)
}

func AddPlayerWithoutObs(session *Session) {
	mgr.AddPlayerWithoutObs(session)
}

func GetPlayerByAddr(addr string) *Session {
	return mgr.GetPlayerByAddr(addr)
}

func GetPlayer(playerId interface{}) *Session {
	return mgr.GetPlayer(playerId)
}

func GetAllPlayers() []*Session {
	return mgr.GetAllPlayers()
}

func GetPlayerCount() int {
	return mgr.GetPlayerCount()
}

func RemovePlayer(playerId interface{}) {
	mgr.RemovePlayer(playerId)
}

// 使会话加入群组
func JoinGroup(ses *Session, name string) {
	mgr.JoinGroup(ses, name)
}

func JoinGroupWithId(playerId interface{}, name string) {
	if ses := mgr.GetPlayer(playerId); nil != ses {
		ses.JoinGroup(name)
	}
}

func QuitGroup(ses *Session, name string) {
	mgr.QuitGroup(ses, name)
}

func QuitGroupWithId(playerId interface{}, name string) {
	if ses := mgr.GetPlayer(playerId); nil != ses {
		ses.QuitGroup(name)
	}
}

func RemoveGroup(name string) {
	mgr.RemoveGroup(name)
}

func GetCountOfGroup(name string) int {
	return mgr.GetCountOfGroup(name)
}

func Update() {
	mgr.Update()
}

func AddObserver(c SessionObserver) {
	mgr.AddObserver(c)
}

func RemoveObserver(c SessionObserver) {
	mgr.RemoveObserver(c)
}

func Notify(buf []byte) {
	mgr.Notify(buf)
}

func NotifyGroup(name string, buf []byte, excepter interface{}) {
	mgr.NotifyGroup(name, buf, excepter)
}

func NotifyGroupExcept(name string, buf []byte, excepters interface{}) {
	mgr.NotifyGroup(name, buf, excepters)
}

func OnFunc(fn func(*Session)) {
	mgr.OnFunc(fn)
}